package cn.blnp.net.flowable.api.task;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * <h3>任务服务API案例演示</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/4/29 11:51
 */
@Slf4j
@SpringBootTest
public class TaskApiTest {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    /**
     *  <p>
     *      TaskService接口主要用于操作正在运行的流程任务，主要提供以下6类方法：
     *  </p>
     *  <hr/>
     *  <ul>
     *      <li>任务实例的创建、保存、查询、删除等，主要操作运行时任务表ACT_RU_TASK；</li>
     *      <li>任务权限相关操作，主要指任务和人员之间的关系管理，可以设置办理人、候选人、候选组，以及其他类型的关系，主要操作运行时身份关系表 ACT_RU_IDENTITYLINK；</li>
     *      <li>任务办理相关操作，包括认领、委托、办理等；</li>
     *      <li>变量管理相关操作，包括变量的新增、删除、查询等，主要操作运行时变量表ACT_RU_ VARIABLE；</li>
     *      <li>任务评论管理相关操作，包括任务评论的新增、删除、查询等，主要操作评论表ACT_HI_COMMENT；</li>
     *      <li>任务附件管理相关操作，包括任务附件的新增、删除、查询等，主要操作附件表ACT_HI_ ATTACHMENT</li>
     *  </ul>
     **/

    /**
     * <p><b>用途:待办任务查询<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 11:57 2025/4/29
     * @params []
     * @param
     * @return void
    **/
    @Test
    public void test() {
        /**
         *      获取用户的待办任务是Flowable常用应用场景之一。待办任务存储在ACT_RU_TASK表中。TaskService接口提供查询ACT_RU_TASK表的方法，因此可以通过TaskService接口
         *  获取用户的待办任务。TaskService接口中提供了2种查询任务实例的方法：一种是通过创建TaskQuery对象传入参数查询，另一种是通过创建NativeTaskQuery对象传入SQL查询。
         *
         *  //通过创建TaskQuery对象传入参数查询
         *  TaskQuery createTaskQuery();
         *
         *  //通过创建NativeTaskQuery对象传入SQL查询
         *  NativeTaskQuery createNativeTaskQuery();
         **/
        List<Task> taskList = taskService.createTaskQuery()
                .taskCandidateOrAssigned("huhaiqin")
                .orderByTaskCreateTime().desc().list();
        if (taskList != null && taskList.size() > 0) {
            for (Task task : taskList) {
                log.warn("ID为{}的任务的办理人为{}创建时间为：{}", task.getId(),
                        task.getAssignee(), DateUtil.formatDateTime(task.getCreateTime())
                );
            }
        } else {
            log.warn("{} 没有待办任务", "huhaiqin");
        }

        /**
         *  演示结果：
         *  2025-04-29 14:24:50.279  INFO 63444 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : huhaiqin 没有待办任务
         **/
    }
    
    /**
     * <p><b>用途:任务办理与权限控制<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 14:26 2025/4/29
     * @params [] 
     * @param  
     * @return void
    **/
    @Test
    public void test1() {
        /**
         *  此处演示使用 请假流程：flow/demo.bpmn20.xml
         **/
        String defId = "demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d";
        //根据流程定义ID发起流程
        ProcessInstance procInst = runtimeService
                .startProcessInstanceById(defId);
        log.warn("发起了一个流程实例，流程实例ID为：{}", procInst.getId());
        //查询第一个任务
        Task firstTask = taskService.createTaskQuery()
                .processInstanceId(procInst.getId()).singleResult();
        log.warn("第一个任务ID为：{}，任务名称为：{}", firstTask.getId(),
                firstTask.getName());
        //完成第一个任务
        taskService.complete(firstTask.getId());
        log.warn("第一个任务办理完成！没有进行权限控制");
        //查询第二个任务
        Task secondTask = taskService.createTaskQuery()
                .processInstanceId(procInst.getId()).singleResult();
        log.warn("第二个任务ID为：{}，任务名称为：{}", secondTask.getId(), secondTask.getName());

        /**
         *  此时对当前任务进行权限控制
         **/
        //设置任务与人员的关系
        taskService.addCandidateUser(secondTask.getId(), "hebo");
        taskService.addCandidateUser(secondTask.getId(), "liuxiaopeng");
        taskService.addUserIdentityLink(secondTask.getId(), "wangjunlin", "participant");

        //办理任务
        MyTaskService myTaskService = new MyTaskService(taskService);
        myTaskService.complete(secondTask.getId(), "huhaiqin");
        myTaskService.complete(secondTask.getId(), "wangjunlin");
        myTaskService.complete(secondTask.getId(), "liuxiaopeng");
        myTaskService.complete(secondTask.getId(), "hebo");

        //因为流程已结束，所以只能通过历史服务来获取任务实例
        log.warn("查询ID为{}的流程实例中任务办理情况", procInst.getId());
        List<HistoricTaskInstance> hisTaskInsts = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(procInst.getId())
                .orderByHistoricTaskInstanceStartTime().asc()
                .list();
        for (HistoricTaskInstance hisTaskInst : hisTaskInsts) {
            log.warn("ID为{}的任务的办理人为 {}，开始时间 {}，结束时间 {}", hisTaskInst.getId(),
                    hisTaskInst.getAssignee(),
                    DateUtil.formatDateTime(hisTaskInst.getCreateTime()),
                    DateUtil.formatDateTime(hisTaskInst.getEndTime())
            );
        }

        /**
         *  演示结果：
         *  2025-04-29 14:44:07.848  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 发起了一个流程实例，流程实例ID为：598d12a2-24c5-11f0-8eb7-50ebf6ec8f6d
         * 2025-04-29 14:44:07.858  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 第一个任务ID为：59906e07-24c5-11f0-8eb7-50ebf6ec8f6d，任务名称为：请假申请
         * 2025-04-29 14:44:07.892  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 第一个任务办理完成！没有进行权限控制
         * 2025-04-29 14:44:07.894  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 第二个任务ID为：5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d，任务名称为：人事审批
         * 2025-04-29 14:44:07.931  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : huhaiqin尝试办理ID为5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d的任务
         * 2025-04-29 14:44:07.937  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : huhaiqin 没有权限查看ID为 5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d 的任务
         * 2025-04-29 14:44:07.937  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : wangjunlin尝试办理ID为5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d的任务
         * 2025-04-29 14:44:07.940  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : wangjunlin 是任务的 participant
         * 2025-04-29 14:44:07.940  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : wangjunlin 没有权限办理ID为 5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d 的任务
         * 2025-04-29 14:44:07.940  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : liuxiaopeng尝试办理ID为5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d的任务
         * 2025-04-29 14:44:07.942  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : liuxiaopeng 是任务的 candidate
         * 2025-04-29 14:44:08.004  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : liuxiaopeng 完成了ID为 5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d 的任务的办理
         * 2025-04-29 14:44:08.004  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : hebo尝试办理ID为5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d的任务
         * 2025-04-29 14:44:08.005  WARN 74064 --- [           main] c.b.net.flowable.api.task.MyTaskService  : 任务不存在或已被其他候选人办理完成
         * 2025-04-29 14:44:08.005  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 查询ID为598d12a2-24c5-11f0-8eb7-50ebf6ec8f6d的流程实例中任务办理情况
         * 2025-04-29 14:44:08.064  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : ID为59906e07-24c5-11f0-8eb7-50ebf6ec8f6d的任务的办理人为 null，开始时间 2025-04-29 14:44:07，结束时间 2025-04-29 14:44:07
         * 2025-04-29 14:44:08.064  WARN 74064 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : ID为5996fdba-24c5-11f0-8eb7-50ebf6ec8f6d的任务的办理人为 liuxiaopeng，开始时间 2025-04-29 14:44:07，结束时间 2025-04-29 14:44:07
         **/
    }
    
    
    /**
     * <p><b>用途:评论和附件管理案例<b></p>
     * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
     * @since 14:48 2025/4/29
     * @params [] 
     * @param  
     * @return void
    **/
    @Test
    public void test2() throws IOException {
        /**
         *  Flowable可以针对流程实例或任务实例添加评论和附件。评论数据存储在ACT_HI_COMMENT表中，附件数据存储在ACT_HI_ATTACHMENT和
         * ACT_GE_BYTEARRAY表中。评论和附件相关的接口都由TaskService提供。核心接口如下所示：
         *
         *  //为流程任务实例或流程实例添加评论
         *  Comment addComment(String taskId, String processInstanceId, String message);
         *
         *  //为流程任务实例或流程实例添加特定类型的评论
         *  Comment addComment(String taskId, String processInstanceId, String type, String message);
         *
         *  //根据评论ID更新评论，如果ID不存在，则抛出异常
         *  void saveComment(Comment comment);
         *
         *  //根据评论ID查询评论，如果ID不存在，则返回null
         *  Comment getComment(String commentId);
         *
         *  //根据任务ID或流程实例ID删除评论
         *  void deleteComments(String taskId, String processInstanceId);
         *
         *  //根据评论ID删除评论
         *  void deleteComment(String commentId);
         *
         *  //根据任务ID查询所有评论
         *  List<Comment> getTaskComments(String taskId);
         *
         *  //根据任务ID和类型查询所有评论
         *  List<Comment> getTaskComments(String taskId, String type);
         *
         *  //查询某一类型的所有评论
         *  List<Comment> getCommentsByType(String type);
         *
         *  //根据流程实例ID查询评论
         *  List<Comment> getProcessInstanceComments(String processInstanceId);
         *
         *  //根据流程实例ID和类型查询评论
         *  List<Comment> getProcessInstanceComments(String processInstanceId, String type);
         *
         *  //创建附件
         *  Attachment createAttachment(String attachmentType, String taskId, String processInstanceId, String attachmentName, String attachmentDescription, InputStream content);
         *
         *  //根据URL创建附件
         *  Attachment createAttachment(String attachmentType, String taskId, String processInstanceId, String attachmentName, String attachmentDescription, String url);
         *
         *  //根据附件ID更新附件
         *  void saveAttachment(Attachment attachment);
         *
         *  //根据附件ID查询附件（不包含附件的具体内容）
         *  Attachment getAttachment(String attachmentId);
         *
         *  //查询附件内容，并将其转换为输入流
         *  InputStream getAttachmentContent(String attachmentId);
         *
         *  //查询任务ID对应的附件列表
         *  List<Attachment> getTaskAttachments(String taskId);
         **/
        String defId = "demoFlow:1:0f45be86-24a8-11f0-a472-50ebf6ec8f6d";
        //根据流程定义ID发起流程
        ProcessInstance procInst = runtimeService
                .startProcessInstanceById(defId);
        log.warn("发起了一个流程实例，流程实例ID为：{}", procInst.getId());
        //评论测试
        Task task = taskService.createTaskQuery()
                .processInstanceId(procInst.getId())
                .singleResult();

        taskService.addComment(task.getId(), task.getProcessInstanceId(), "任务【" + task.getName() + "】:添加评论");
        taskService.complete(task.getId());

        task = taskService.createTaskQuery()
                .processInstanceId(procInst.getId())
                .singleResult();
        taskService.addComment(task.getId(), task.getProcessInstanceId(), "任务【" + task.getName() + "】:添加评论");

        //查询所有的评论、意见
        List<Comment> taskComments = taskService.getProcessInstanceComments(task.getProcessInstanceId());
        for (Comment taskComment : taskComments) {
            log.error(taskComment.getFullMessage());
        }
        //删除所有评论
        taskService.deleteComments(null, task.getProcessInstanceId());
        taskComments = taskService.getProcessInstanceComments(task.getProcessInstanceId());
        if (taskComments.isEmpty()) {
            log.error("该流程实例已经没有评论了");
        }

        //附件测试
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("flow/demo.bpmn20.xml");
        taskService.createAttachment("", task.getId(),
                task.getProcessInstanceId(),
                "测试附件",
                "测试附件描述",
                inputStream);
        List<Attachment> taskAttachments = taskService.getTaskAttachments(task.getId());
        for (Attachment taskAttachment : taskAttachments) {
            log.info(taskAttachment.getName());
            InputStream attachmentContent =
                    taskService.getAttachmentContent(taskAttachment.getId());
            log.warn("附件字节数：{}", attachmentContent.available());
        }

        /**
         *  演示结果：
         *  2025-04-29 14:56:19.072  WARN 20220 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 发起了一个流程实例，流程实例ID为：0d62196a-24c7-11f0-8d2a-50ebf6ec8f6d
         * 2025-04-29 14:56:19.154 ERROR 20220 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 任务【人事审批】:添加评论
         * 2025-04-29 14:56:19.154 ERROR 20220 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 任务【请假申请】:添加评论
         * 2025-04-29 14:56:19.169 ERROR 20220 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 该流程实例已经没有评论了
         * 2025-04-29 14:56:19.195  INFO 20220 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 测试附件
         * 2025-04-29 14:56:19.200  WARN 20220 --- [           main] c.b.net.flowable.api.task.TaskApiTest    : 附件字节数：4045
         **/
    }
}
